use std::path::PathBuf;

use futures::TryFutureExt;

use aspect_ident::{crypto, IdentSigner};

use crate::errors::Error;

pub struct SignerManager {
    path: PathBuf,
    signer: aspect_ident::IdentSigner,
}

impl SignerManager {
    /// Opens an ident signer at a particular path.
    pub async fn open(path: PathBuf) -> Result<Self, Error> {
        let signer_buf = tokio::fs::read(&path)
            .map_err(|e| Error::IoAt(path.clone(), e))
            .await?;
        let signer =
            serde_json::from_slice(&signer_buf).map_err(|e| Error::SerdeJson("IdentSigner", e))?;
        Ok(Self { path, signer })
    }

    /// Creates a signer manager by writing an ident signer to a path.
    pub async fn create(path: PathBuf, signer: aspect_ident::IdentSigner) -> Result<Self, Error> {
        let signer_buf =
            serde_json::to_vec(&signer).map_err(|e| Error::SerdeJson("IdentSigner", e))?;
        tokio::fs::write(&path, &signer_buf)
            .map_err(|e| Error::IoAt(path.clone(), e))
            .await?;
        Ok(Self { path, signer })
    }

    /// Creates an update and writes it to disk.
    pub async fn update_ident_package(
        &mut self,
        comment: String,
    ) -> Result<aspect_ident::DirPackage, Error> {
        // Make a clone since this modifies the signer.
        let mut new_signer = self.signer.clone();
        let pkg = new_signer.create_update(comment);

        // Write it to disk.
        save_signer(&new_signer, &self.path).await?;

        // If successful, *then* we can overwrite and return the update.
        self.signer = new_signer;
        Ok(pkg)
    }

    /// Signs a generic message with for particular purpose.
    pub fn sign_message(&self, purpose: crypto::SigPurpose, msg: &[u8]) -> crypto::Signature {
        self.signer.sign_generic_message(purpose, msg)
    }

    pub fn current_update_pkg(&self) -> &aspect_ident::UpdatePackage {
        self.signer.last_update()
    }

    pub fn current_dirpkg(&self) -> &aspect_ident::DirPackage {
        self.signer.cur_package()
    }

    /// Extracts a copy of the signer, shouldn't be used for signing new identity updates.
    // TODO remove this, it's bad
    pub fn get_internal_signer(&self) -> IdentSigner {
        self.signer.clone()
    }
}

async fn save_signer(signer: &aspect_ident::IdentSigner, path: &PathBuf) -> Result<(), Error> {
    let enc = serde_json::to_vec(signer).expect("signer_manager: serialize");
    tokio::fs::write(path, enc)
        .map_err(|e| Error::IoAt(path.clone(), e))
        .await?;
    Ok(())
}
